package org.sql.statement.builder;

import org.sql.statement.util.StatementUtil;

public class WhereClause implements Constants {

    enum LogicConnector {
        AND, OR;
    }

    enum Op {

        BETWEEN("BETWEEN", "AND"), EQ("="), GT(">"), GT_EQ(">="), IN("IN"), IS_NULL("IS NULL"), LT("<"), LT_EQ("<="), NEQ("!="), NIN("NOT IN"), NOT_NULL(
                "NOT NULL");

        String[] signs = null;

        Op(String... signs) {
            this.signs = signs;
        }

        public String[] getSigns() {
            return signs;
        }
    }

    private String fieldName = null;

    private LogicConnector logicConnector = null;

    private Op operator = null;

    private Object[] values = null;

    public WhereClause(String fieldName, Op operation, LogicConnector logicConnector, Object... values) {
        this.fieldName = fieldName;
        this.operator = operation;
        this.values = values;
        this.logicConnector = logicConnector;
    }

    public WhereClause(String fieldName, Op operation, Object... values) {
        this(fieldName, operation, LogicConnector.AND, values);
    }

    private StringBuilder betweenOperatorElement(WhereClause clause) {
        return new StringBuilder().append(clause.fieldName).append(" ").append(clause.operator.getSigns()[0]).append(" ").append("?").append(" ")
                .append(clause.operator.getSigns()[1]).append(" ").append("?");
    }

    public String build() {
        final StringBuilder sb;

        switch (this.operator) {
        case EQ:
        case NEQ:
        case LT:
        case GT:
        case LT_EQ:
        case GT_EQ:
            sb = simpleOperatorElement(this);
            break;
        case IN:
        case NIN:
            sb = inOperatorElement(this);
            break;
        case IS_NULL:
        case NOT_NULL:
            sb = nullOperatorElement(this);
            break;
        case BETWEEN:
            sb = betweenOperatorElement(this);
            break;
        default:
            throw new RuntimeException("No operator set!");
        }

        return sb.toString();
    }

    public String getFieldName() {
        return fieldName;
    }

    private StringBuilder getLogicOperatorIfNeeded() {
        final StringBuilder sb = new StringBuilder();

        if (this.logicConnector != null)
            sb.append(" ").append(this.logicConnector).append(" ");

        return sb;
    }

    private Object getMarks(int length) {
        final StringBuilder sb = new StringBuilder();

        for (int i = 0; i < length; i++)
            sb.append("?").append(STR_COMMA_SPACE);

        return StatementUtil.getAllButLast(sb, STR_COMMA_SPACE.length());
    }

    public Op getOperation() {
        return operator;
    }

    public Object[] getValues() {
        return values;
    }

    private StringBuilder inOperatorElement(WhereClause clause) {
        return getLogicOperatorIfNeeded().append(clause.fieldName).append(" ").append(clause.operator.getSigns()[0]).append(" ").append("(")
                .append(getMarks(clause.values.length)).append(")");
    }

    private StringBuilder nullOperatorElement(WhereClause clause) {
        return getLogicOperatorIfNeeded().append(clause.fieldName).append(" ").append(clause.operator.getSigns()[0]);
    }

    private StringBuilder simpleOperatorElement(final WhereClause clause) {
        return getLogicOperatorIfNeeded().append(clause.fieldName).append(" ").append(clause.operator.getSigns()[0]).append(" ").append("?");
    }

}